
#include <iostream>
#include <string>
#include <map>
#include <algorithm>
#include <vector>
using namespace std;
/*
Given a string, find the length of the longest substring without repeating characters.
Examples:
Given "abcabcbb", the answer is "abc", which the length is 3.
Given "bbbbb", the answer is "b", with the length of 1.
Given "pwwkew", the answer is "wke", with the length of 3. Note that the answer must be a substring, "pwke" is a subsequence and not a substring.
*/

class Solution {
public:	
	//using map
	int lengthOfLongestSubstring(string s) {
		int ans = 0;
		std::map<char, int> hashmap;

		for (int j = 0, i = 0; j < s.length(); j++) {
			if (hashmap.count(s[j])) {
				i = std::max(hashmap.find(s[j])->second, i);
				hashmap.find(s[j])->second = j + 1;
			}
			else {
				hashmap.insert(pair<char, int>(s[j], j + 1));
			}
			ans = std::max(ans, j - i + 1);

		}
		return ans;
	}

	int lengthOfLongestSubstring1(string s) {
		map<char, int> m;
		int maxLen = 0;
		int lastRepeatPos = -1;
		string substring;

		for (unsigned int i = 0; i<s.size(); i++) {
			if (m.find(s[i]) != m.end() && lastRepeatPos < m[s[i]]) {
				lastRepeatPos = m[s[i]];
			}
			
			if (i - lastRepeatPos > maxLen) {
				maxLen = i - lastRepeatPos;
			}
			m[s[i]] = i;
		}
		substring = s.substr(lastRepeatPos == -1 ? 0 : lastRepeatPos, maxLen);
		cout << "substring:" << substring << endl;
		cout << "lastRepeatPos:" << lastRepeatPos << endl;
		return maxLen;
	}

	//don't use <map>
	int lengthOfLongestSubstring2(string s) {
		const int MAX_CHARS = 256;
		int m[MAX_CHARS];
		memset(m, -1, sizeof(m));

		int maxLen = 0;
		int lastRepeatPos = -1;
		for (unsigned int i = 0; i<s.size(); i++) {
			if (m[s[i]] != -1 && lastRepeatPos < m[s[i]]) {
				lastRepeatPos = m[s[i]];
			}
			if (i - lastRepeatPos > maxLen) {
				maxLen = i - lastRepeatPos;
			}
			m[s[i]] = i;
		}
		return maxLen;
	}	

	int lengthOfLongestSubstring3(string s) {
		vector<int> dict(256, -1);
		int maxLen = 0, start = -1;
		for (int i = 0; i != s.length(); i++) {
			if (dict[s[i]] > start)
				start = dict[s[i]];
			dict[s[i]] = i;
			maxLen = max(maxLen, i - start);
		}
		return maxLen;
	}

	int lengthOfLongestSubstring4(string s) {
		// for ASCII char sequence, use this as a hashmap
		vector<int> charIndex(256, -1);
		int longest = 0, m = 0;

		for (int i = 0; i < s.length(); i++) {
			m = max(charIndex[s[i]] + 1, m);    // automatically takes care of -1 case
			charIndex[s[i]] = i;
			longest = max(longest, i - m + 1);
		}

		return longest;
	}

	/**
	* Solution (DP, O(n)):
	*
	* Assume L[i] = s[m...i], denotes the longest substring without repeating
	* characters that ends up at s[i], and we keep a hashmap for every
	* characters between m ... i, while storing <character, index> in the
	* hashmap.
	* We know that each character will appear only once.
	* Then to find s[i+1]:
	* 1) if s[i+1] does not appear in hashmap
	*    we can just add s[i+1] to hash map. and L[i+1] = s[m...i+1]
	* 2) if s[i+1] exists in hashmap, and the hashmap value (the index) is k
	*    let m = max(m, k), then L[i+1] = s[m...i+1], we also need to update
	*    entry in hashmap to mark the latest occurency of s[i+1].
	*
	* Since we scan the string for only once, and the 'm' will also move from
	* beginning to end for at most once. Overall complexity is O(n).
	*
	* If characters are all in ASCII, we could use array to mimic hashmap.
	*/
};


int _lengthOfLongestSubstring()
{
	Solution solu;
	string raw = "pwwkew";//"bbbbb";// "abcabcbb";
	int len = 0;
	
	len = solu.lengthOfLongestSubstring1(raw);

	cout << "raw:" << raw << endl;
	cout << "lengthOfLongestSubstring:" << len << endl;
	return 0;
}